﻿using System;
using CoreRay;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CoreRay.Testing
{
    /// <summary>
    /// This is a test class for TriangleTest and is intended
    /// to contain all TriangleTest Unit Tests
    /// </summary>
    [TestClass]
    public class TriangleTest
    {

        /// <summary>
        /// A test for Constructor with a null GeometryBuffer object parameter.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ConstructorNullGeometryBufferTest()
        {
            // Execute
            Triangle triangle = new Triangle(null, 1);
        }

        /// <summary>
        /// A test for Constructor with an out of range triangle index.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void ConstructorTriangleIndexOutOfRangeTest()
        {
            // Setup
            GeometryBuffer geometryBuffer = SetupGeometryBuffer();

            // Execute
            Triangle triangle = new Triangle(geometryBuffer, 6);
        }

        /// <summary>
        /// A test for vertex properties.
        /// </summary>
        [TestMethod]
        public void ConstructorVertexPropertiesTest()
        {
            // Setup
            GeometryBuffer geometryBuffer = SetupGeometryBuffer();

            // Execute
            Triangle triangle = new Triangle(geometryBuffer, 1);

            // Verify
            Assert.AreEqual(new Vector3(0, 0, 0), triangle.Vertex1Position);
            Assert.AreEqual(new Vector3(0, 1, 0), triangle.Vertex2Position);
            Assert.AreEqual(new Vector3(1, 1, 0), triangle.Vertex3Position);
        }

        /// <summary>
        /// A test for BoundingBox property.
        /// </summary>
        [TestMethod]
        public void BoundingBoxPropertyTest()
        {
            // Setup
            GeometryBuffer geometryBuffer = SetupGeometryBuffer();

            // Execute
            Triangle triangle = new Triangle(geometryBuffer, 1);

            BoundingBox expected = new BoundingBox(new Vector3(0, 0, 0), new Vector3(1, 1, 0));
            BoundingBox actual = triangle.BoundingBox;

            // Verify
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        /// A test for Intersection with an intersection result.
        /// </summary>
        [TestMethod]
        public void IntersectionGoodTest()
        {
            // Setup
            GeometryBuffer geometryBuffer = SetupGeometryBuffer();

            Ray ray = new Ray(new Vector3(0.5f, 0.5f, -1), new Vector3(0, 0, 1));

            // Execute
            Triangle triangle = new Triangle(geometryBuffer, 1);

            Intersection intersection = triangle.Intersect(ray);

            Vector3 expected = new Vector3(0.5f, 0.5f, 0.0f);
            Vector3 actual = intersection.DifferentialGeometry.Position;

            // Verify
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        /// A test for Intersection with a negative intersection result.
        /// </summary>
        [TestMethod]
        public void IntersectionBadTest()
        {
            // Setup
            GeometryBuffer geometryBuffer = SetupGeometryBuffer();

            Ray ray = new Ray(new Vector3(1.5f, 0.5f, -1), new Vector3(0, 0, 1));

            // Execute
            Triangle triangle = new Triangle(geometryBuffer, 1);

            Intersection intersection = triangle.Intersect(ray);

            // Verify
            Assert.IsNull(intersection);
        }

        /// <summary>
        /// A test for Intersection Position.
        /// </summary>
        [TestMethod]
        public void IntersectionPositionGoodTest()
        {
            // Setup
            GeometryBuffer geometryBuffer = SetupGeometryBuffer();

            Ray ray = new Ray(new Vector3(0.5f, 0.5f, -1), new Vector3(0, 0, 1));

            // Execute
            Triangle triangle = new Triangle(geometryBuffer, 1);
            bool result = triangle.IntersectPosition(ray);

            // Verify
            Assert.IsTrue(result);
        }

        /// <summary>
        /// A test for Intersection Position.
        /// </summary>
        [TestMethod]
        public void IntersectionPositionBadTest()
        {
            // Setup
            GeometryBuffer geometryBuffer = SetupGeometryBuffer();

            Ray ray = new Ray(new Vector3(1.5f, 0.5f, -1), new Vector3(0, 0, 1));

            // Execute
            Triangle triangle = new Triangle(geometryBuffer, 1);
            bool result = triangle.IntersectPosition(ray);

            // Verify
            Assert.IsFalse(result);
        }

        /// <summary>
        /// A test for Intersection with an intersection result with texturecoordinates.
        /// </summary>
        [TestMethod]
        public void IntersectionTextureCoordinatesTest()
        {
            // Setup
            VertexFormat vertexFormat = new VertexFormat();
            vertexFormat.AddElement("Position", typeof(Vector3));
            vertexFormat.AddElement("TextureCoordinates", typeof(Vector2));

            GeometryBuffer geometryBuffer = new GeometryBuffer(vertexFormat);

            geometryBuffer.VertexBuffer.Write(new Vector3(5, 6, 2));
            geometryBuffer.VertexBuffer.Write(new Vector2(0, 0));
            geometryBuffer.VertexBuffer.Write(new Vector3(-97, 23, 25));
            geometryBuffer.VertexBuffer.Write(new Vector2(1, 0));
            geometryBuffer.VertexBuffer.Write(new Vector3(57, 68, 147));
            geometryBuffer.VertexBuffer.Write(new Vector2(1, 1));

            geometryBuffer.VertexBuffer.Write(new Vector3(0, 0, 0));
            geometryBuffer.VertexBuffer.Write(new Vector2(0, 0));
            geometryBuffer.VertexBuffer.Write(new Vector3(0, 1, 0));
            geometryBuffer.VertexBuffer.Write(new Vector2(1, 0));
            geometryBuffer.VertexBuffer.Write(new Vector3(1, 1, 0));
            geometryBuffer.VertexBuffer.Write(new Vector2(1, 1));

            geometryBuffer.IndexBuffer.Write(0);
            geometryBuffer.IndexBuffer.Write(1);
            geometryBuffer.IndexBuffer.Write(2);

            geometryBuffer.IndexBuffer.Write(3);
            geometryBuffer.IndexBuffer.Write(4);
            geometryBuffer.IndexBuffer.Write(5);

            Ray ray = new Ray(new Vector3(0.5f, 0.5f, -1), new Vector3(0, 0, 1));

            // Execute
            Triangle triangle = new Triangle(geometryBuffer, 1);

            Intersection intersection = triangle.Intersect(ray);

            Vector3 expected = new Vector3(0.5f, 0.5f, 0.0f);
            Vector3 actual = intersection.DifferentialGeometry.Position;

            // Verify
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        /// Setup method used for the tests.
        /// </summary>
        /// <returns>Test GeometryBuffer.</returns>
        private GeometryBuffer SetupGeometryBuffer()
        {
            // Init vertex format
            VertexFormat vertexFormat = new VertexFormat();
            vertexFormat.AddElement("Position", typeof(Vector3));

            // Init geometry buffer
            GeometryBuffer geometryBuffer = new GeometryBuffer(vertexFormat);

            // Init vertex buffer
            geometryBuffer.VertexBuffer.Write(new Vector3(5, 6, 2));
            geometryBuffer.VertexBuffer.Write(new Vector3(-97, 23, 25));
            geometryBuffer.VertexBuffer.Write(new Vector3(57, 68, 147));

            geometryBuffer.VertexBuffer.Write(new Vector3(0, 0, 0));
            geometryBuffer.VertexBuffer.Write(new Vector3(0, 1, 0));
            geometryBuffer.VertexBuffer.Write(new Vector3(1, 1, 0));

            // Init index buffer
            geometryBuffer.IndexBuffer.Write(0);
            geometryBuffer.IndexBuffer.Write(1);
            geometryBuffer.IndexBuffer.Write(2);

            geometryBuffer.IndexBuffer.Write(3);
            geometryBuffer.IndexBuffer.Write(4);
            geometryBuffer.IndexBuffer.Write(5);

            return geometryBuffer;
        }
    }
}
